package org.andengine.entity.scene.menu.animator;

import org.andengine.entity.scene.menu.MenuScene;
import org.andengine.entity.scene.menu.item.IMenuItem;
import org.andengine.util.adt.align.HorizontalAlign;
import org.andengine.util.adt.align.VerticalAlign;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga Inc.
 *
 * @author Nicolas Gramlich
 * @since 11:17:32 - 02.04.2010
 */
public abstract class MenuSceneAnimator implements IMenuSceneAnimator {
    // ===========================================================
    // Constants
    // ===========================================================

    private static final HorizontalAlign HORIZONTALALIGN_DEFAULT = HorizontalAlign.CENTER;
    private static final VerticalAlign VERTICALALIGN_DEFAULT = VerticalAlign.CENTER;
    private static final float SPACING_DEFAULT = 1.0f;
    protected float mMenuItemSpacing = MenuSceneAnimator.SPACING_DEFAULT;
    private static final float OFFSET_X_DEFAULT = 0.0f;

    // ===========================================================
    // Fields
    // ===========================================================
    protected float mOffsetX = MenuSceneAnimator.OFFSET_X_DEFAULT;
    private static final float OFFSET_Y_DEFAULT = 0.0f;
    protected float mOffsetY = MenuSceneAnimator.OFFSET_Y_DEFAULT;
    protected HorizontalAlign mHorizontalAlign;
    protected VerticalAlign mVerticalAlign;
    protected float mOverallHeight;

    // ===========================================================
    // Constructors
    // ===========================================================

    public MenuSceneAnimator() {
        this(MenuSceneAnimator.HORIZONTALALIGN_DEFAULT, MenuSceneAnimator.VERTICALALIGN_DEFAULT);
    }

    public MenuSceneAnimator(final HorizontalAlign pHorizontalAlign, final VerticalAlign pVerticalAlign) {
        this.mHorizontalAlign = pHorizontalAlign;
        this.mVerticalAlign = pVerticalAlign;
    }

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    @Override
    public float getOffsetX() {
        return this.mOffsetX;
    }

    @Override
    public void setOffsetX(final float pOffsetX) {
        this.mOffsetX = pOffsetX;
    }

    @Override
    public float getOffsetY() {
        return this.mOffsetY;
    }

    @Override
    public void setOffsetY(final float pOffsetY) {
        this.mOffsetY = pOffsetY;
    }

    @Override
    public float getMenuItemSpacing() {
        return this.mMenuItemSpacing;
    }

    @Override
    public void setMenuItemSpacing(final float pMenuItemSpacing) {
        this.mMenuItemSpacing = pMenuItemSpacing;
    }

    @Override
    public HorizontalAlign getHorizontalAlign() {
        return this.mHorizontalAlign;
    }

    @Override
    public void setHorizontalAlign(final HorizontalAlign pHorizontalAlign) {
        this.mHorizontalAlign = pHorizontalAlign;
    }

    @Override
    public VerticalAlign getVerticalAlign() {
        return this.mVerticalAlign;
    }

    @Override
    public void setVerticalAlign(final VerticalAlign pVerticalAlign) {
        this.mVerticalAlign = pVerticalAlign;
    }

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    protected abstract void onMenuItemPositionBuilt(final MenuScene pMenuScene, final int pIndex, final IMenuItem pMenuItem, final float pX, final float pY);

    protected abstract void onMenuItemPositionReset(final MenuScene pMenuScene, final int pIndex, final IMenuItem pMenuItem, final float pX, final float pY);

    @Override
    public void buildMenuSceneAnimations(final MenuScene pMenuScene) {
        this.updateOverallHeight(pMenuScene);

        final int menuItemCount = pMenuScene.getMenuItemCount();

        for (int i = 0; i < menuItemCount; i++) {
            final IMenuItem menuItem = pMenuScene.getMenuItem(i);

            final float x = this.getMenuItemX(pMenuScene, i);
            final float y = this.getMenuItemY(pMenuScene, i);
            this.onMenuItemPositionBuilt(pMenuScene, i, menuItem, x, y);
        }
    }

    @Override
    public void resetMenuSceneAnimations(final MenuScene pMenuScene) {
        final int menuItemCount = pMenuScene.getMenuItemCount();

        for (int i = 0; i < menuItemCount; i++) {
            final IMenuItem menuItem = pMenuScene.getMenuItem(i);

            final float x = this.getMenuItemX(pMenuScene, i);
            final float y = this.getMenuItemY(pMenuScene, i);
            this.onMenuItemPositionReset(pMenuScene, i, menuItem, x, y);
        }
    }

    // ===========================================================
    // Methods
    // ===========================================================

    protected float getMenuItemX(final MenuScene pMenuScene, final int pIndex) {
        final float menuSceneWidth = pMenuScene.getWidth();

        final IMenuItem menuItem = pMenuScene.getMenuItem(pIndex);
        final float menuItemWidth = menuItem.getWidth();

		/* Determine horizontal position. */
        final float x;
        switch (this.mHorizontalAlign) {
            case LEFT:
                x = menuItemWidth * 0.5f;
                break;
            case CENTER:
                x = menuSceneWidth * 0.5f;
                break;
            case RIGHT:
                x = menuSceneWidth - (menuItemWidth * 0.5f);
                break;
            default:
                throw new IllegalArgumentException("Unexpected " + HorizontalAlign.class.getSimpleName() + " with value: '" + this.mHorizontalAlign + "'.");
        }

        return x + this.mOffsetX;
    }

    protected float getMenuItemY(final MenuScene pMenuScene, final int pIndex) {
        final float menuSceneHeight = pMenuScene.getHeight();

        final IMenuItem menuItem = pMenuScene.getMenuItem(pIndex);

		/* Prepare vertical position. */
        float baseY;
        switch (this.mVerticalAlign) {
            case TOP:
                baseY = menuSceneHeight;
                break;
            case CENTER:
                baseY = (0.5f * menuSceneHeight) + (this.mOverallHeight * 0.5f);
                break;
            case BOTTOM:
                baseY = this.mOverallHeight;
                break;
            default:
                throw new IllegalArgumentException("Unexpected " + VerticalAlign.class.getSimpleName() + " with value: '" + this.mVerticalAlign + "'.");
        }

        final float menuItemHeight = menuItem.getHeight();

		/* Determine vertical position. */
        final float y = baseY - (menuItemHeight * 0.5f) - (pIndex * (menuItemHeight + this.mMenuItemSpacing));

        return y + this.mOffsetY;
    }

    private void updateOverallHeight(final MenuScene pMenuScene) {
        final int menuItemCount = pMenuScene.getMenuItemCount();

        float overallHeight = 0;
        for (int i = menuItemCount - 1; i >= 0; i--) {
            final IMenuItem menuItem = pMenuScene.getMenuItem(i);
            overallHeight += menuItem.getHeight();
        }

        this.mOverallHeight = overallHeight + ((menuItemCount - 1) * this.mMenuItemSpacing);
    }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================
}
